package com.zerowidth.moc.web.auth.interceptor;

import com.alibaba.fastjson.JSONObject;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.zerowidth.moc.web.auth.TokenHelper;
import com.zerowidth.moc.web.auth.annotation.NeedToken;
import com.zerowidth.moc.web.auth.annotation.NoToken;
import com.zerowidth.moc.web.resp.ResCode;
import com.zerowidth.moc.web.resp.ResultBean;
import com.zerowidth.db.bean.UserBean;
import com.zerowidth.db.op.AuthTokenDB;
import com.zerowidth.db.op.UserDB;
import com.zerowidth.db.utils.IOUtils;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;

public class AuthInterceptor implements HandlerInterceptor {

    /**
     *
     * @return true:此方法没有完成处理请求，需要继续交给controller处理
     *         false:此方法完成处理请求，不需要继续交给controller处理
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        String url = request.getRequestURI().toString();

        response.setCharacterEncoding(StandardCharsets.UTF_8.name());
        response.setContentType("application/json; charset=utf-8");

        // 如果不是映射到方法直接通过
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }

        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();

        //检查是否有 PassToken 注释，有则跳过认证
        if (method.isAnnotationPresent(NoToken.class)) {
            NoToken noToken = method.getAnnotation(NoToken.class);
            if (noToken.required()) {
                return true;
            }
        }

        //没有写 NeedToken注解时，认为 NeedToken=true; 即需要token
        boolean needTokenDefault = true;
        if (needTokenDefault) {
            if (method.isAnnotationPresent(NeedToken.class)) {
                NeedToken userLoginToken = method.getAnnotation(NeedToken.class);
                if (!userLoginToken.required()) {
                    //如果强制声明 needToken = false，即不需要token
                    return true;
                }
            }

            String token = request.getHeader("authToken");

            // 执行 token 认证
            if (token == null) {
                writeNoAuthResponse(response, ResCode.ER_1000, "未知用户，没有操作权限");
                return false;
            }

            if (AuthTokenDB.findUserByToken(token) == null) {
                writeNoAuthResponse(response, ResCode.ER_1003, "登录已失效，请重新登录");
                return false;
            }

            // 获取 token 中的 user id
            String userId = "";
            try {
                userId = TokenHelper.getUserIdByToken(token);
            } catch (JWTDecodeException j) {
                writeNoAuthResponse(response, ResCode.ER_1003, "登录已失效，请重新登录");
                return false;
            }
            UserBean user = UserDB.findUserByUserId(userId);
            if (user == null) {
                writeNoAuthResponse(response, ResCode.ER_1001, "用户不存在，请重新登录");
                return false;
            }

            //注释此段代码，不再验证 token 有效期
            // 验证 token
//            JWTVerifier jwtVerifier = TokenHelper.getVerifier(user);
//            try {
//                jwtVerifier.verify(token);
//            } catch (JWTVerificationException e) {
//                writeNoAuthResponse(response, ResCode.ER_1002, "token已失效，请重新登录");
//                return false;
//            }
        }

        return true;
    }

    private void writeNoAuthResponse(HttpServletResponse response, int code, String errorMsg) throws IOException {
        response.setCharacterEncoding(StandardCharsets.UTF_8.name());
        response.setContentType("application/json; charset=utf-8");

        ResultBean<Object> singFailBean = new ResultBean<>();
        singFailBean.code = code;
        singFailBean.message = errorMsg;

        PrintWriter writer = null;

        try {
            writer = response.getWriter();
            writer.print(JSONObject.toJSONString(singFailBean));

        } catch (Exception e) {

        } finally {
            IOUtils.close(writer);
        }
    }

}
